package com.xyy.saas.payment.adpater.impl;

import com.alibaba.fastjson.JSON;
import com.alipay.api.AlipayClient;
import com.alipay.api.domain.AlipayTradeCloseModel;
import com.alipay.api.domain.AlipayTradeRefundModel;
import com.alipay.api.request.AlipayTradeCloseRequest;
import com.alipay.api.request.AlipayTradeRefundRequest;
import com.alipay.api.response.AlipayTradeCloseResponse;
import com.alipay.api.response.AlipayTradeRefundResponse;
import com.xyy.framework.redis.core.RedisClient;
import com.xyy.saas.payment.adpater.PaymentAdapter;
import com.xyy.saas.payment.adpater.aliPayHandler.AliPayChannelHandler;
import com.xyy.saas.payment.adpater.channel.setting.ChannelSettingManager;
import com.xyy.saas.payment.callback.core.result.CallbackRunner;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.constants.ChannelConstants;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.OrderClosedBo;
import com.xyy.saas.payment.cores.bo.PayBo;
import com.xyy.saas.payment.cores.bo.PrepayBo;
import com.xyy.saas.payment.cores.bo.TransactionResultBO;
import com.xyy.saas.payment.cores.enums.PayModeEnum;
import com.xyy.saas.payment.cores.enums.ResultCodeEnum;
import com.xyy.saas.payment.cores.enums.TransactionTypeEnum;
import com.xyy.saas.payment.cores.param.TransactionParam;
import com.xyy.saas.payment.cores.vo.CallbackResultVo;
import com.xyy.saas.payment.dao.model.PrePayOrder;
import com.xyy.saas.payment.dao.model.RefundOrder;
import com.xyy.saas.payment.dao.model.ThirdUserInfo;
import com.xyy.saas.payment.payment.core.bo.RefundBO;
import com.xyy.saas.payment.payment.core.dto.OrderClosedDto;
import com.xyy.saas.payment.payment.core.dto.PayDto;
import com.xyy.saas.payment.payment.core.dto.PrepayDto;
import com.xyy.saas.payment.payment.core.dto.RefundDto;
import com.xyy.saas.payment.payment.core.service.PrePayOrderService;
import com.xyy.saas.payment.service.ThirdUserInfoService;
import com.xyy.saas.payment.util.ApolloUtil;
import com.xyy.saas.payment.util.JSONUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * @Author zhangwenxiong
 * @Date 2020/11/24 15:47
 * @Version 1.0
 */
@Service
public class AliCommonAdapter implements PaymentAdapter, InitializingBean {
    private static final Logger LOGGER = LoggerFactory.getLogger(AliCommonAdapter.class);

    public static Map<String, Object> map = new HashMap<>();

    @Autowired
    private RedisClient redisClient;

    @Autowired
    private CallbackRunner callbackRunner;

    @Autowired
    private PrePayOrderService prePayOrderService;

    @Autowired
    private ChannelSettingManager channelSettingManager;

    @Autowired
    private ApplicationContext applicationContext;

    @Resource
    private ThirdUserInfoService thirdUserInfoService;

    private Map<String,AliPayChannelHandler> aliPayChannelHandlerMap = new HashMap<>();

    @Override
    public PrepayBo prepay(PrepayDto prepayDto) throws PaymentException {
        return prePayOrderService.prepayOrderByPrepayDto(prepayDto);
    }

    //推荐调用支付接口直接调用这个方法
    @Override
    public PayBo pay(PayDto payDto) throws PaymentException {
        PayBo payBo = aliPayChannelHandlerMap.get(payDto.getPayMode()).pay(payDto);
        //订单关闭调用需要的参数
        redisClient.setEx(Constants.ORDER_CLOSE_KEY+payDto.getChannel()+payDto.getPayNo(),payDto.getBusinessOrderType()+":"+payDto.getPayMode(),86400);
        return payBo;
    }

    @Override
    public TransactionResultBO queryTransaction(TransactionParam param) throws PaymentException {
        if(TransactionTypeEnum.PAY.getCode().equals(param.getTransactionType())) {
            TransactionResultBO transactionResultBO = aliPayChannelHandlerMap.get(param.getPayMode()).queryPay(param);
            transactionResultBO.setChannel(ChannelConstants.CHANNEL_DIRECT_NAME+"_"+ChannelConstants.CHANNEL_ALI_NAME);
            return transactionResultBO;
        } else if(TransactionTypeEnum.REFUND.getCode().equals(param.getTransactionType())) {
            return aliPayChannelHandlerMap.get(param.getPayMode()).queryRefund(param);
        } else {
            throw new PaymentException(ResultCodeEnum.UN_SUPPORT_METHOD);
        }
    }

    @Override
    public RefundBO refund(RefundDto refundDto) throws PaymentException {
        RefundBO refundBO =  new RefundBO();
        AlipayTradeRefundResponse response;
        try {
            AlipayClient client = null;

            if(StringUtils.isNotBlank(refundDto.getMerchantId())) {
                client = channelSettingManager.getAlipayClient(refundDto.getMerchantId());
            }
            else {
                ThirdUserInfo thirdUserInfo = thirdUserInfoService.getThirdUserInfoByUserId(refundDto.getReceiverId());

                if(thirdUserInfo != null && Constants.PINGAN.equals(thirdUserInfo.getThirdType())) {
                    client = channelSettingManager.getAlipayClient(refundDto.getReceiverId(), refundDto.getBusinessOrderType() + "_" + Constants.PINGAN, refundDto.getPayMode());
                }
                // POP大商户已切平安
                else if(Boolean.parseBoolean(ApolloUtil.getProperty(Constants.EC_POP_BIG_SHOP_TO_PINGAN_SWITCH, Boolean.FALSE.toString())) &&
                        Objects.equals(ApolloUtil.getProperty(Constants.POP_DIRECT_PAY_YBM_TECH_USER_ID), refundDto.getReceiverId()) &&
                        PayModeEnum.MINI.getMode().equals(refundDto.getPayMode()))
                {
                    client = channelSettingManager.getAlipayClient(refundDto.getReceiverId(), refundDto.getBusinessOrderType() + "_" + Constants.PINGAN, refundDto.getPayMode());
                }
                else {
                    client = channelSettingManager.getAlipayClient(refundDto.getReceiverId(), refundDto.getBusinessOrderType(), refundDto.getPayMode());
                }
            }

            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
            AlipayTradeRefundModel refundModel = new AlipayTradeRefundModel();
            refundModel.setOutTradeNo(refundDto.getPayNo());
            refundModel.setRefundAmount(new BigDecimal(refundDto.getAmount()).divide(new BigDecimal(100)).toString());
            refundModel.setOutRequestNo(refundDto.getRefundNo());
            refundModel.setRefundReason(refundDto.getDescription());
            request.setBizModel(refundModel);
            response = client.execute(request);
        } catch (Exception e) {
            LOGGER.error("AliCommonAdapter#refund create failed refundDto:{}", JSON.toJSONString(refundDto), e);
            throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
        }
        if(response.isSuccess()){
            if("10000".equals(response.getCode()) && "Y".equals(response.getFundChange())) {
                refundBO.setRefundNo(refundDto.getRefundNo());
                refundBO.setChannelRefundNo(refundDto.getRefundNo());
                refundBO.setStatus(RefundOrder.STATUS_FINISH);
                refundBO.setDate(response.getGmtRefundPay());
                Long unixTime = response.getGmtRefundPay().getTime() / 1000;
                LOGGER.info("AliCommonAdapter#refund create success pay_no:{},result:{}", refundDto.getPayNo(), JSON.toJSONString(response));
                callbackRunner.aliRefundCallback(refundDto.getRefundNo(), CallbackResultVo.TYPE_REFUND_SUCCESS, refundDto.getAmount(),0, unixTime, refundDto.getCallbackUrl());
                return refundBO;
            } else {
                LOGGER.error("AliCommonAdapter#refund create failed refundDto:{},result:{}", JSON.toJSONString(refundDto), JSON.toJSONString(response));
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
            }
        } else {
            LOGGER.error("AliCommonAdapter#refund response not success refundDto:{} response:{}", JSONUtils.toJSON(refundDto), JSONUtils.toJSON(response));
            if(response.getSubMsg().contains("余额不足")) {
                throw new PaymentException(ResultCodeEnum.CHANNEL_TODAY_MONEY_NOT_ENOUGH);
            }else {
                LOGGER.error("AliCommonAdapter#refund create failed refundDto:{},result:{}", JSON.toJSONString(refundDto), JSON.toJSONString(response));
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR);
            }
        }
    }

    @Override
    public OrderClosedBo orderClosed(OrderClosedDto orderClosedDto) throws PaymentException {
        OrderClosedBo closedBo = new OrderClosedBo();
        closedBo.setPayNo(orderClosedDto.getPayNo());
        closedBo.setStatus(OrderClosedBo.ERROR);
        //从redis获取发起支付时的缓存,用于下面查询微信配置
        String typeAndMode = redisClient.get(Constants.ORDER_CLOSE_KEY+orderClosedDto.getChannel()+orderClosedDto.getPayNo());
        if (StringUtils.isEmpty(typeAndMode)){
            LOGGER.info("AliCommonAdapter orderClosed end get from redis typeAndMode:{},orderClosedDto:{}", JSON.toJSONString(typeAndMode),JSON.toJSONString(orderClosedDto));
            throw new PaymentException(ResultCodeEnum.ORDER_NOT_PAID_ERROR);
        }
        //查询预支付单
        PrePayOrder prePayOrderByPayNo = prePayOrderService.getPrePayOrderByPayNo(orderClosedDto.getPayNo());
        //从redis取出的缓存格式是type:mode
        String[] arr = typeAndMode.split(":");
        LOGGER.info("AliCommonAdapter orderClosed begin typeAndMode:{},orderClosedDto:{}", typeAndMode,JSON.toJSONString(orderClosedDto));
        AlipayClient client = channelSettingManager.getAlipayClient(null == prePayOrderByPayNo ? "" : prePayOrderByPayNo.getReceiverId(), arr[0],arr[1]);
        //获取请求体
        AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
        if (null != client){
            AlipayTradeCloseModel model = new AlipayTradeCloseModel();
            model.setOutTradeNo(orderClosedDto.getPayNo());
            request.setBizModel(model);
            try {
                AlipayTradeCloseResponse response = client.execute(request);
                if(response.isSuccess()){
                    closedBo.setStatus(OrderClosedBo.SUCCESS);
                }else {
                    LOGGER.warn("AliCommonAdapter orderClosed fail PayNo:{}",orderClosedDto.getPayNo());
                    throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR,response.getMsg());
                }
                LOGGER.info("AliCommonAdapter orderClosed end PayNo:{}", orderClosedDto.getPayNo());
            }catch (Exception e){
                LOGGER.error("AliCommonAdapter orderClosed ali error payNo:{}, exception:{}", orderClosedDto.getPayNo(),e);
                throw new PaymentException(ResultCodeEnum.CHANNEL_CONNECT_ERROR.getCode(), ResultCodeEnum.CHANNEL_CONNECT_ERROR.getMsg());
            }
        }
        return closedBo;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Map<String, AliPayChannelHandler> aliChannel = applicationContext.getBeansOfType(AliPayChannelHandler.class);
        for (AliPayChannelHandler item: aliChannel.values()) {
            aliPayChannelHandlerMap.put(item.getMethod(), item);
        }
    }
}
